%%%-------------------------------------------------------------------
%%% File    : rdf_rs.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created :  7 May 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(rdf_rs).

-include("../include/rdf_rs.hrl"). 

-include("../include/rdf_spalg.hrl").
-include("../include/valg.hrl").
-include("../include/rdf_record.hrl"). 
-include("../include/pipe.hrl"). 

%% API
-export([
		 new_mapped/3,
		 join_lookup_by/3,
		 vect_by_tag/1,
		 project/2,
		 test/0,
		 test/1
		]).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: 
%% Description:
%%-------------------------------------------------------------------
new_mapped({?QUAD_TAG_SA,_S,_P,_O,_G}=QuadPatt,
	LookupConst,
	VarName
   ) when ?IS_VAR(VarName) ->
	LokupConstRole=value_role_ex(LookupConst, QuadPatt),
	VarRole=value_role_ex(VarName, QuadPatt),
	VExpr=vect_expr(VarRole,LokupConstRole,QuadPatt),
	#rs{
		expr={?MAP_TAG,VExpr,
			  fun(Rec) -> 
					  {'$rs',_Key=element(2,Rec),Rec} 
			  end },
		key_var=VarName,
		quad_patterns=[QuadPatt]
	}.

join_lookup_by(
  #rs{expr=Expr,key_var=KeyVar,quad_patterns=QPList}=_CoreRs,
  {?QUAD_TAG_SA,_S,_P,_O,_G}=QuadPatt,
  VarName
			  ) when ?IS_VAR(VarName)->
	{Expr1,KeyVar1}=case VarName of
						KeyVar ->%no remap case
							{Expr,KeyVar};
						NewKeyVar ->%remap case
							{TuplePos,ElemPos}=case value_pos(NewKeyVar,QPList,_TPosStart=3) of
												   false -> exit({bad_join_var,NewKeyVar});
												   Pos -> Pos
											   end,
							MExpr={?MAP_TAG,Expr,
								   fun(Rec) -> %{'$rs',Key,Tup1,...,TupN}
										   NewKey=element(ElemPos,element(TuplePos,Rec)),
										   erlang:setelement(2, Rec, NewKey)
								   end},
							{MExpr,NewKeyVar}
					end,
	VarRole=value_role_ex(VarName, QuadPatt),
	VExpr=vect_expr(VarRole,QuadPatt),
	JExpr={?JOIN_LKP_TAG,Expr1,[VExpr],
		   _RecFun=fun([RsCore,QRs]) ->
						   erlang:append_element(RsCore, QRs)
				   end},
	#rs{
		expr=JExpr,
		key_var=KeyVar1,
		quad_patterns=[QuadPatt|QPList]
	   };
join_lookup_by(
  #rs{expr=Expr,key_var=KeyVar,quad_patterns=QPList}=_CoreRs,
  QList,
  VarName) when is_list(QList)->
	{Expr1,KeyVar1}=case VarName of
						KeyVar ->%no remap case
							{Expr,KeyVar};
						NewKeyVar ->%remap case
							{TuplePos,ElemPos}=case value_pos(NewKeyVar,QPList,_TPosStart=3) of
												   false -> exit({bad_join_var,NewKeyVar});
												   Pos -> Pos
											   end,
							MExpr={?MAP_TAG,Expr,
								   fun(Rec) -> %{'$rs',Key,Tup1,...,TupN}
										   NewKey=element(ElemPos,element(TuplePos,Rec)),
										   erlang:setelement(2, Rec, NewKey)
								   end},
							{MExpr,NewKeyVar}
					end,
	VExprList=[begin
				   VarRole=value_role_ex(VarName, QuadPatt),
				   vect_expr(VarRole,QuadPatt)
			   end || QuadPatt<-QList],
	JExpr={?JOIN_LKP_TAG,Expr1,VExprList,
%% 		   _RecFun=fun({RsCore,QRs}) ->
%% 						   erlang:append_element(RsCore, QRs)
%% 				   end
		   _RecFun=fun([RsCore|QRs]) ->
						   lists:foldl(fun(QR,Tup)->
											   erlang:append_element(Tup, QR)
									   end, RsCore, QRs)
						   
				   end
		  },
	#rs{
		expr=JExpr,
		key_var=KeyVar1,
		quad_patterns=lists:reverse(QList)++QPList
	   }.
	
project(#rs{expr=Expr,key_var=KeyVar,quad_patterns=QPList},VarList) ->
	Positions=[value_pos(Var, QPList, 3)||Var<-VarList],
	{?FORMAT_TAG,Expr, 
	 _RecFun=fun(Rec)->
					 lists:foldl(fun({TuplePos,ElemPos},NewRec) ->
										 erlang:append_element(NewRec, element(ElemPos,element(TuplePos,Rec)))
								 end, {_Tag=element(1,Rec),_Key=element(2,Rec)}, Positions)
			 end}.

%%====================================================================
%% Internal functions
%%====================================================================
value_role_ex(VarName, QuadPatt) when element(1,QuadPatt)==?QUAD_TAG_SA ->
	case value_role(VarName, QuadPatt) of
		false ->
			exit({no_such_var,VarName,QuadPatt});
		R -> R
	end.
value_role(Var,{?QUAD_TAG_SA,Var,_P,_O,_G}) ->
	s;
value_role(Var,{?QUAD_TAG_SA,_S,Var,_O,_G}) ->
	p;
value_role(Var,{?QUAD_TAG_SA,_S,_P,Var,_G}) ->
	o;
value_role(Var,{?QUAD_TAG_SA,_S,_P,_O,Var}) ->
	g;
value_role(_Var,_Q) ->
	false.

vect_expr(VarRole,LokupConstRole,QuadPatt) ->
	RecTagConst=rdf_record:tag_by_node(LokupConstRole),
%% 	RecTagVar=rdf_record:tag_by_node(VarRole),
	{?VECT_TAG,_Vect=vect_by_tag(RecTagConst),
	 {lookup,const_at(QuadPatt, LokupConstRole)},
	 _RecFun=fun(Quad) -> %% TODO : implement feature in valg!!!
%% 					 match(rdf_record:as(RecTagVar, Quad),QuadPatt)
					 match(Quad,QuadPatt)
			 end}.
vect_expr(VarRole,QuadPatt) ->
	RecTag=rdf_record:tag_by_node(VarRole),
	{?VECT_TAG,_Vect=vect_by_tag(RecTag),
	 _RecFun=fun(Quad) -> %% TODO : implement feature in valg!!!
			 match(Quad,QuadPatt)
	 end}.
match(Q,QPatt) ->
	?SPOG=rdf_record:to_norm(Q),
	match(?SPOG,QPatt,5).
match(Quad,_QuadPatt,1) ->
	Quad;
match(Quad,QuadPatt,Pos) ->
	case element(Pos,QuadPatt) of
		Val when ?IS_VAR(Val) ->
			match(Quad,QuadPatt,Pos-1);
		Val ->
			if 
				Val==element(Pos,Quad) ->
					match(Quad,QuadPatt,Pos-1);
				true ->
					?REJECT_REC
			end
	end.

value_pos(Val,QPList,StartPos) ->
	seek_value(Val,lists:reverse(QPList),StartPos).

seek_value(_Val,[]=_QPList,_QPos) ->
	false;
seek_value(Val,[QP|Tail]=_QPList,QPos) ->
	case value_role(Val,QP) of
		false ->
			seek_value(Val,Tail,QPos+1);
		Role ->
			{QPos,1+rdf_record:role_pos(Role)}
	end.
	

varpos_of({?QUAD_TAG_SA,S,P,O,G}) ->
	lists:filter(fun({_,Val})-> ?IS_VAR(Val)end, [{s,S},{p,P},{o,O},{g,G}]).

var_at(Q,NodeRole) ->
	value_at(Q,NodeRole,var).
const_at(Q,NodeRole) ->
	value_at(Q,NodeRole,const).
value_at(Q,NodeRole,const) ->
	case element(1+rdf_record:role_pos(NodeRole),Q) of
		Val when ?IS_VAR(Val) ->
			exit({not_a_const,Val});
		Val when ?IS_CONST(Val) ->
			Val
	end;
value_at(Q,NodeRole,var) ->
	case element(1+rdf_record:role_pos(NodeRole),Q) of
		Val when ?IS_VAR(Val) ->
			Val;
		Val when ?IS_CONST(Val) ->
			exit({not_a_var,Val})
	end.

vect_by_tag(Tag) ->
%% 	{vect,Tag}.
	case svc_vms:get_vect(Tag) of
		{error,_}=ERR ->
			exit(ERR);
		Vect ->
			Vect
	end.
	
%%====================================================================
%% Internal functions
%%====================================================================

test() ->
	S=subj0,
	S1=subj1,
	Name=name,
	Name1=name1,
	Friend=friend,
	Rs=new_mapped({?QUAD_TAG_SA,S,{onto,"name"},Name,'_'},{onto,"name"},S),
	Rs1=join_lookup_by(Rs, {?QUAD_TAG_SA,S,{onto,"friend"},Friend,'_'}, S),
	Rs2=join_lookup_by(Rs1, {?QUAD_TAG_SA,Friend,{onto,"name"},Name1,'_'}, Friend),
	join_lookup_by(Rs2, {?QUAD_TAG_SA,S1,{onto,"name"},Name1,'_'}, Name1).
test(list) ->
	S=subj0,
	Age=age,
	Name=name,
	Family=family,
	FriendId=friend,
	VasyaLit={'$lit',"Vasya",string},
	Rs=new_mapped({?QUAD_TAG_SA,S,{onto,"name"},VasyaLit,'_'}, VasyaLit, S),
	Rs1=join_lookup_by(Rs, {?QUAD_TAG_SA,S,{onto,"friend"},FriendId,'_'}, S),
	_Rs2=join_lookup_by(Rs1, [{?QUAD_TAG_SA,FriendId,{onto,"name"},Name,'_'},
							{?QUAD_TAG_SA,FriendId,{onto,"familyName"},Family,'_'}, 
							{?QUAD_TAG_SA,FriendId,{onto,"age"},Age,'_'}], FriendId).

